Package g4p_controls

Source Code of g4p_controls.GSlider2D

/*
  Part of the GUI for Processing library
    http://www.lagers.org.uk/g4p/index.html
  http://gui4processing.googlecode.com/svn/trunk/

  Copyright (c) 2013 Peter Lager

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
*/

package g4p_controls;

import g4p_controls.HotSpot.HSrect;

import java.awt.RenderingHints;

import processing.core.PApplet;
import processing.core.PGraphicsJava2D;
import processing.event.MouseEvent;

/**
* This slider is used to control 2 variables by dragging the thumb over
* a 2D surface. It has all the features of the standard slider (GSlider)
* except that it does not have ticks or stick-to-ticks functionality. <br>
* If no limits are set then the control will return a value in the range
* 0.0 to 1.0 for both the x and the y axis. The setXlimits and setYlimits
* can be used to set a different range for each axis independently. <b>
*
* The minimum size for this control is 40x40 pixels and this is enforced
* when the control is created. If necessary the width and/or height the
* rectangle will be increased to 40pixels. <br>
*
* @author Peter Lager
*
*/
public class GSlider2D extends GValueControl2D {

  // Palette index constants
  static int DBORDER = 1, LBORDER = 3, BACK = 6;
  static int TBORDER = 15, TOFF = 3, TOVER = 11, TDOWN = 14, TDRAG = 15;

  static final float THUMB_SIZE = 10;
  static final float HALF_THUMB_SIZE = THUMB_SIZE / 2;
  static final float BORDER_WIDTH = 2;
 
  // Define the drag area for this control
  protected float dragWidth, dragHeight, dragD;
 
  protected int downHotSpot = -1;
  // Mouse over status
  protected int status = OFF_CONTROL;
 
  protected float startXlimit = 0, endXlimit = 1;
  protected float startYlimit = 0, endYlimit = 1;
 
  /**
   * Create a 2D slider inside the specified rectangle.
   * @param theApplet
   * @param p0
   * @param p1
   * @param p2
   * @param p3
   */
  public GSlider2D(PApplet theApplet, float p0, float p1, float p2, float p3) {
    super(theApplet, p0, p1, p2, p3);
    // Enforce minimum size constraint
    if(width < 40 || height < 40)
      resize(PApplet.max(width,40), PApplet.max(height,40));
 
    dragWidth = width - THUMB_SIZE - 2 * BORDER_WIDTH;
    dragHeight = height - THUMB_SIZE - 2 * BORDER_WIDTH;
    dragD = 2 + THUMB_SIZE/2;
   
    buffer = (PGraphicsJava2D) winApp.createGraphics((int)width, (int)height, PApplet.JAVA2D);
    buffer.g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
        RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
    buffer.g2.setFont(G4P.numericLabelFont);
    hotspots = new HotSpot[]{
        new HSrect(THUMB_SPOT, dragD - HALF_THUMB_SIZE + parametricPosX * dragWidth,
            dragD - HALF_THUMB_SIZE + parametricPosY * dragHeight, THUMB_SIZE, THUMB_SIZE )// thumb
        new HSrect(TRACK_SPOT, dragD, dragD, dragWidth, dragHeight)    // track
    };
    z = Z_SLIPPY;

    epsilon = 0.98f / PApplet.max(dragWidth, dragHeight);
    opaque = true;
   
    // Now register control with applet
    createEventHandler(G4P.sketchApplet, "handleSlider2DEvents",
        new Class<?>[]{ GSlider2D.class, GEvent.class },
        new String[]{ "slider2d", "event" }
    );
    registeredMethods = PRE_METHOD | DRAW_METHOD | MOUSE_METHOD;
    cursorOver = HAND;
    G4P.addControl(this);
  }

  /**
   * Updates thumb hotspot due changes caused by easing
   */
  protected void updateDueToValueChanging(){
    hotspots[0].x = dragD - HALF_THUMB_SIZE  + parametricPosX * dragWidth;
    hotspots[0].y = dragD  - HALF_THUMB_SIZE + parametricPosY * dragHeight; 
  }

 
  /**
   * Set the amount of easing to be used when a value is changing. The default value
   * is 1 (no easing) values > 1 will cause the value to rush from its starting value
   * and decelerate towards its final values. In other words it smoothes the movement
   * of the slider thumb or knob rotation.
   *
   * @param easeBy the easing to set
   */
  public void setEasing(float easeBy) {
    easing = (easeBy < 1) ? 1 : easeBy;
  }

  /**
   * X (horz) limits
   * Sets the range of values to be returned. This method will
   * assume that you want to set the valueType to INTEGER
   *
   * @param start the start value of the range
   * @param end the end value of the range
   */
  public void setXlimits(int start, int end){
    startXlimit = start;
    endXlimit = end;
    setEpsilon();
    valueType = INTEGER;
    bufferInvalid = true;
  }
 
  /**
   * X (horz) limits
   * Sets the initial value and the range of values to be returned. This
   * method will assume that you want to set the valueType to INTEGER.
   *
   * @param initValue the initial value
   * @param start the start value of the range
   * @param end the end value of the range
   */
  public void setLimitsX(int initValue, int start, int end){
    startXlimit = start;
    endXlimit = end;
    valueType = INTEGER;
    setEpsilon();
    bufferInvalid = true;
    setValueX(initValue);
    updateDueToValueChanging();
  }
 
  /**
   * X (horz) limits
   * Sets the range of values to be returned. This method will
   * assume that you want to set the valueType to DECIMAL
   *
   * @param start
   * @param end
   */
  public void setLimitsX(float start, float end){
    startXlimit = start;
    endXlimit = end;
    if(valueType == INTEGER){
      valueType = DECIMAL;
      setPrecision(1);
    }
    setEpsilon();
    bufferInvalid = true;
  }
 
  /**
   * X (horz) limits
   * Sets the initial value and the range of values to be returned. This
   * method will assume that you want to set the valueType to DECIMAL.
   *
   * @param initValue the initial value
   * @param start the start value of the range
   * @param end the end value of the range
   */
  public void setLimitsX(float initValue, float start, float end){
    startXlimit = start;
    endXlimit = end;
    initValue = PApplet.constrain(initValue, start, end);
    if(valueType == INTEGER){
      valueType = DECIMAL;
      setPrecision(1);
    }
    setEpsilon();
    bufferInvalid = true;
    setValueX(initValue);
    updateDueToValueChanging();
  }

  /**
   * Y (vert) limits
   * Sets the range of values to be returned. This method will
   * assume that you want to set the valueType to INTEGER
   *
   * @param start the start value of the range
   * @param end the end value of the range
   */
  public void setLimitsY(int start, int end){
    startYlimit = start;
    endYlimit = end;
    setEpsilon();
    valueType = INTEGER;
    bufferInvalid = true;
  }
 
  /**
   * Y (vert) limits
   * Sets the initial value and the range of values to be returned. This
   * method will assume that you want to set the valueType to INTEGER.
   *
   * @param initValue the initial value
   * @param start the start value of the range
   * @param end the end value of the range
   */
  public void setLimitsY(int initValue, int start, int end){
    startYlimit = start;
    endYlimit = end;
    valueType = INTEGER;
    setEpsilon();
    bufferInvalid = true;
    setValueY(initValue);
    updateDueToValueChanging();
  }
 
  /**
   * Y (vert) limits
   * Sets the range of values to be returned. This method will
   * assume that you want to set the valueType to DECIMAL
   *
   * @param start
   * @param end
   */
  public void setLimitsY(float start, float end){
    startYlimit = start;
    endYlimit = end;
    if(valueType == INTEGER){
      valueType = DECIMAL;
      setPrecision(1);
    }
    setEpsilon();
    bufferInvalid = true;
  }
 
  /**
   * Y (vert) limits
   * Sets the initial value and the range of values to be returned. This
   * method will assume that you want to set the valueType to DECIMAL.
   *
   * @param initValue the initial value
   * @param start the start value of the range
   * @param end the end value of the range
   */
  public void setLimitsY(float initValue, float start, float end){
    startYlimit = start;
    endYlimit = end;
    initValue = PApplet.constrain(initValue, start, end);
    if(valueType == INTEGER){
      valueType = DECIMAL;
      setPrecision(1);
    }
    setEpsilon();
    bufferInvalid = true;
    setValueY(initValue);
    updateDueToValueChanging();
  }

  /**
   * Set the X (horz) value for the slider. <br>
   * The value supplied will be constrained to the current limits.
   * @param v the new value
   */
  public void setValueX(float v){
    if(valueType == INTEGER)
      v = Math.round(v);
    float t = (v - startXlimit) / (endXlimit - startXlimit);
    t = PApplet.constrain(t, 0.0f, 1.0f);
    parametricTargetX = t;
  }
 
  /**
   * Set the Y (vert) value for the slider. <br>
   * The value supplied will be constrained to the current limits.
   * @param v the new value
   */
  public void setValueY(float v){
    if(valueType == INTEGER)
      v = Math.round(v);
    float t = (v - startYlimit) / (endYlimit - startYlimit);
    t = PApplet.constrain(t, 0.0f, 1.0f);
    parametricTargetY = t;
  }
 
  /**
   * Set both the XY values for the slider. <br>
   * The values supplied will be constrained to the appropriate current limits.
   * @param vx the new X (horz) value
   * @param vy the new Y (vert) value
   */
  public void setValueXY(float vx, float vy){
    setValueX(vx);
    setValueY(vy);
  }
 
  /**
   * Get the current X value as a float
   */
  public float getValueXF(){
    return startXlimit + (endXlimit - startXlimit) * parametricPosX;
  }

  /**
   * Get the current X value as an integer. <br>
   * DECIMAL and EXPONENT value types will be rounded to the nearest integer.
   */
  public int getValueXI(){
    return Math.round(getValueXF());
  }
 
  /**
   * Get the current X value as a string taking into account the number format. <br>
   */
  public String getValueXS(){
    return getNumericDisplayString(getValueXF());
  }
 
  /**
   * Get the current Y value as a float
   */
  public float getValueYF(){
    return startYlimit + (endYlimit - startYlimit) * parametricPosY;
  }

  /**
   * Get the current Y value as an integer. <br>
   * DECIMAL and EXPONENT value types will be rounded to the nearest integer.
   */
  public int getValueYI(){
    return Math.round(getValueYF());
  }
 
  /**
   * Get the current Y value as a string taking into account the number format. <br>
   */
  public String getValueYS(){
    return getNumericDisplayString(getValueYF());
  }
 
  /**
   * Used to format the number into a string for display.
   * @param number
   * @return the number formated as a string
   */
  protected String getNumericDisplayString(float number){
    String s = "";
    switch(valueType){
    case INTEGER:
      s = String.format("%d", Math.round(number));
      break;
    case DECIMAL:
      s = String.format("%." + precision + "f", number);
      break;
    case EXPONENT:
      s = String.format("%." + precision + "e", number);
      break;
    }
    return s.trim();
  }

  /**
   * For DECIMAL values this sets the number of decimal places to
   * be displayed.
   * @param nd must be >= 1 otherwise will use 1
   */
  public void setPrecision(int nd){
    nd = PApplet.constrain(nd, 1, 5);
    if(nd < 1)
      nd = 1;
    if(nd != precision){
      precision = nd;
      setEpsilon();
      bufferInvalid = true;
    }
  }
 
  /**
   * Set the numberFormat and precision in one go. <br>
   * Valid number formats are INTEGER, DECIMAL, EXPONENT <br>
   * Precision must be >= 1 and is ignored for INTEGER.
   *
   * @param numberFormat G4P.INTEGER, G4P.DECIMAL orG4P. EXPONENT
   * @param precision must be >= 1
   */
  public void setNumberFormat(int numberFormat, int precision){
    switch(numberFormat){
    case INTEGER:
    case DECIMAL:
    case EXPONENT:
      this.valueType = numberFormat;
      break;
    default:
      valueType = DECIMAL;
    }
    setPrecision(precision);
    bufferInvalid = true;
  }
   
  /**
   * Set the numberFormat and precision in one go. <br>
   * Valid number formats are INTEGER, DECIMAL, EXPONENT <br>
   * Precision must be >= 1 and is ignored for INTEGER.
   *
   * @param numberFormat G4P.INTEGER, G4P.DECIMAL or G4P.EXPONENT
   */
  public void setNumberFormat(int numberFormat){
    switch(numberFormat){
    case INTEGER:
    case DECIMAL:
    case EXPONENT:
      this.valueType = numberFormat;
      break;
    default:
      valueType = DECIMAL;
    }
    bufferInvalid = true;
  }
 
  public void mouseEvent(MouseEvent event){
    if(!visible || !enabled || !available) return;

    calcTransformedOrigin(winApp.mouseX, winApp.mouseY);
    currSpot = whichHotSpot(ox, oy);
    // Make ox,oy relative to top-left of drag area
    ox -= dragD;
    oy -= dragD;
   
    if(currSpot >= 0 || focusIsWith == this)
      cursorIsOver = this;
    else if(cursorIsOver == this)
      cursorIsOver = null;

    switch(event.getAction()){
    case MouseEvent.PRESS:
      if(focusIsWith != this && currSpot > -1 && z >= focusObjectZ()){
        downHotSpot = currSpot;
        status = (downHotSpot == THUMB_SPOT) ? PRESS_CONTROL : OFF_CONTROL;
        offsetH = ox - parametricPosX * dragWidth; // normalised
        offsetV = oy - parametricPosY * dragHeight; // normalised
        takeFocus();
        bufferInvalid = true;
      }
      break;
    case MouseEvent.CLICK:
      if(focusIsWith == this){
        parametricTargetX = ox / dragWidth;
        parametricTargetY = oy / dragHeight;
        dragging = false;
        status = OFF_CONTROL;
        loseFocus(null);
        bufferInvalid = true;
      }
      break;
    case MouseEvent.RELEASE:
      if(focusIsWith == this && dragging){
        if(downHotSpot == THUMB_SPOT){
          mouseUpdateTargets();
        }
        status = OFF_CONTROL;
        bufferInvalid = true;
        loseFocus(null);       
      }
      dragging = false;
      break;
    case MouseEvent.DRAG:
      if(focusIsWith == this){
        status = DRAG_CONTROL;
        dragging = true;
        if(downHotSpot == THUMB_SPOT){
          mouseUpdateTargets();
          bufferInvalid = true;
        }
      }
      break;
    case MouseEvent.MOVE:
      int currStatus = status;
      // If dragged state will stay as PRESSED
      if(currSpot == THUMB_SPOT)
        status = OVER_CONTROL;
      else
        status = OFF_CONTROL;
      if(currStatus != status)
        bufferInvalid = true;
      break;     
    }
  }

  /**
   * Convenience method called during mouse event handling
   */
  private void mouseUpdateTargets(){
    parametricTargetX = ox / dragWidth;
    if(parametricTargetX < 0){
      parametricTargetX = 0;
      offsetH = 0;
    }
    else if(parametricTargetX > 1){
      parametricTargetX = 1;
      offsetH = 0;
    }
    parametricTargetY = oy / dragHeight;
    if(parametricTargetY < 0){
      parametricTargetY = 0;
      offsetV = 0;
    }
    else if(parametricTargetY > 1){
      parametricTargetY = 1;
      offsetV = 0;
    }
  }
 
  public void draw(){
    if(!visible) return;
    // Update buffer if invalid
    updateBuffer();
    winApp.pushStyle();

    winApp.pushMatrix();
    // Perform the rotation
    winApp.translate(cx, cy);
    winApp.rotate(rotAngle);
    winApp.pushMatrix();
    // Move matrix to line up with top-left corner
    winApp.translate(-halfWidth, -halfHeight);
    // Draw buffer
    winApp.imageMode(PApplet.CORNER);
    if(alphaLevel < 255)
      winApp.tint(TINT_FOR_ALPHA, alphaLevel);
    winApp.image(buffer, 0, 0)
    winApp.popMatrix();
    winApp.popMatrix();

    winApp.popStyle();
  }
 
  protected void updateBuffer(){
    if(bufferInvalid) {
      bufferInvalid = false;
      buffer.beginDraw();

      buffer.ellipseMode(PApplet.CENTER);
      buffer.rectMode(PApplet.CENTER);
      // Back ground colour
      if(opaque == true)
        buffer.background(palette[BACK]);
      else
        buffer.background(buffer.color(255,0));

      buffer.pushMatrix();
      // Draw thumb cursor lines
      float tx = dragD + parametricPosX * dragWidth;
      float ty = dragD + parametricPosY * dragHeight;
      buffer.stroke(palette[TBORDER]);
      buffer.strokeWeight(1);
      buffer.line(0, ty, width, ty);
      buffer.line(tx, 0, tx, height);
      switch(status){
      case OFF_CONTROL:
        buffer.fill(palette[TOFF]);
        break;
      case OVER_CONTROL:
        buffer.fill(palette[TOVER]);
        break;
      case PRESS_CONTROL:
        buffer.fill(palette[TDOWN]);
        break;
      case DRAG_CONTROL:
        buffer.fill(palette[TDRAG]);
        break;
      }
      buffer.rect(tx, ty, THUMB_SIZE, THUMB_SIZE);
     
      // Draw control border
      buffer.rectMode(PApplet.CORNERS);
      buffer.noFill();
      buffer.stroke(palette[LBORDER]);
      buffer.strokeWeight(2);
      buffer.rect(0,0,width-1,height-1);
      buffer.stroke(palette[DBORDER]);
      buffer.strokeWeight(1);
      buffer.rect(1,1,width-1,height-1);
     
      buffer.popMatrix();
      buffer.endDraw();
    }
  }
}
TOP

Related Classes of g4p_controls.GSlider2D

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.